package com.example.mqtt.server.netty;

import io.netty.channel.*;
import io.netty.handler.codec.mqtt.MqttFixedHeader;
import io.netty.handler.codec.mqtt.MqttMessage;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;

/**
 * MQTT服务端I/O数据读写处理类
 */
@ChannelHandler.Sharable
public class MqttChannelInboundHandler extends SimpleChannelInboundHandler<MqttMessage> {

    private final Logger log = LoggerFactory.getLogger(this.getClass());

    /**
     * 从客户端收到新的数据时，这个方法会在收到消息时被调用
     *
     * @param channelHandlerContext channelHandler上下文
     * @param mqttMessage MQTT消息
     */
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, MqttMessage mqttMessage) {
        if (null == mqttMessage) {
            return;
        }

        // 获取MQTT固定头
        MqttFixedHeader mqttFixedHeader = mqttMessage.fixedHeader();
        log.info("MQTT接收到的发送类型 {}", mqttFixedHeader.messageType());
        log.info("MQTT消息 {}", mqttMessage);

        Channel channel = channelHandlerContext.channel();

        // 消息类型
        switch (mqttFixedHeader.messageType()) {
            //	在一个网络连接上，客户端只能发送一次CONNECT报文。服务端必须将客户端发送的第二个CONNECT报文当作协议违规处理并断开客户端的连接
            //	建议connect消息单独处理，用来对客户端进行认证管理等 这里直接返回一个CONNACK消息
            case CONNECT:
                MqttMsgBack.connectAck(channel, mqttMessage);
                break;
            //	客户端发布消息 QoS=0\1\2行为不同
            case PUBLISH:
                MqttMsgBack.publishAck(channel, mqttMessage);
                break;
            //	发布释放 QoS=2
            case PUBREL:
                MqttMsgBack.pubComp(channel, mqttMessage);
                break;
            //	客户端订阅主题
            case SUBSCRIBE:
                //	客户端向服务端发送SUBSCRIBE报文用于创建一个或多个订阅，每个订阅注册客户端关心的一个或多个主题。
                //	为了将应用消息转发给与那些订阅匹配的主题，服务端发送PUBLISH报文给客户端。
                //	SUBSCRIBE报文也（为每个订阅）指定了最大的QoS等级，服务端根据这个发送应用消息给客户端
                MqttMsgBack.subscribeAck(channelHandlerContext, channel, mqttMessage);
                break;
            //	客户端取消订阅
            case UNSUBSCRIBE:
                //	客户端发送UNSUBSCRIBE报文给服务端，用于取消订阅主题
                MqttMsgBack.unsubscribeAck(channelHandlerContext, channel, mqttMessage);
                break;
            //	客户端发起心跳
            case PINGREQ:
                //	客户端发送PINGREQ报文给服务端的
                //	在没有任何其它控制报文从客户端发给服务的时，告知服务端客户端还活着
                //	请求服务端发送 响应确认它还活着，使用网络以确认网络连接没有断开
                MqttMsgBack.pingResp(channel);
                break;
            //	客户端主动断开连接
            case DISCONNECT:
                //	DISCONNECT报文是客户端发给服务端的最后一个控制报文， 服务端必须验证所有的保留位都被设置为0
                break;
            default:
                break;
        }
    }
}
